// Jeremy Cowan and Nicholas Lineback

#include "cache.h"
#include <iostream>

//==========================================================
// cache(numBlocks)
// OVERVIEW: constructor, creates cache with numBlocks lines
//==========================================================
cache::cache(int numBlocks)
{
  totalHits = 0;
  totalAccesses = 0;
  mNumBlocks = numBlocks;
  theCache = new int[numBlocks];
  for(int i = 0; i < numBlocks;i++)
  {
    theCache[i] = -1;
  }
  lru = new int[numBlocks];
  for( int i = 0; i < numBlocks; i++)
  {
    lru[i] = 0;
  }
  theEnd = false;
}
//==========================================================
// setEnd(val)
// OVERVIEW: whether a cache is the last level
// MODIFIES: this.theEnd
//==========================================================
void cache::setEnd(bool val)
{
  theEnd = val;
  return;
}

//==========================================================
// read(address)
// OVERVIEW: attempts to read address from cache
// RETURNS: true, if a hit, false if a miss
// MODIFIES: this.theCache, this.LRU
//==========================================================
bool cache::read(int address)
{
  int line;
  line = (address/(gBlockSize*gWordSize))%(mNumBlocks/gAssociativity);
  totalAccesses ++;
  for(int i = 0; i < gAssociativity; i++)
  {
    if(theCache[i*mNumBlocks/gAssociativity + line] == address/(gBlockSize*gWordSize))
    {
      totalHits++;
      lru[i*mNumBlocks/gAssociativity + line] = 0;
      return true;
    }
  }
  
  return false;
}

//==========================================================
// write(address)
// OVERVIEW: writes address to cache
// MODIFIES: this.theCache, this.LRU
//==========================================================
void cache::write(int address)
{
  int 
    line,
    lruIdx,
    maxLRU;
 
  totalAccesses++;
  
  line = (address/(gBlockSize*gWordSize))%(mNumBlocks/gAssociativity);
  
  for(int i = 0; i < gAssociativity; i++)
  {
    if(theCache[i*mNumBlocks/gAssociativity + line] == address/(gBlockSize*gWordSize))
    {
      lru[i*mNumBlocks/gAssociativity + line] = 0;
      return;
    }
  }
   
  lruIdx = line;
  maxLRU = lru[line];

  for(int i = 1; i < gAssociativity; i++)
  {
    if(lru[i*mNumBlocks/gAssociativity + line] > maxLRU)
    {
      lruIdx = i*mNumBlocks/gAssociativity + line;
      maxLRU = lru[lruIdx];
    }
  }
  theCache[lruIdx] = address/(gBlockSize*gWordSize);
  lru[lruIdx] = 0;
}

//==========================================================
// write_up(address)
// OVERVIEW: writes data from lower level of cache to higher level
// MODIFIES: this.theCache, this.LRU
//==========================================================
void cache::write_up(int address)
{
  int 
    line,
    lruIdx,
    maxLRU;
 
  line = (address/(gBlockSize*gWordSize))%(mNumBlocks/gAssociativity);
  
  for(int i = 0; i < gAssociativity; i++)
  {
    if(theCache[i*mNumBlocks/gAssociativity + line] == address/(gBlockSize*gWordSize))
    {
      lru[i*mNumBlocks/gAssociativity + line] = 0;
      return;
    }
  }
   
  lruIdx = line;
  maxLRU = lru[line];

  for(int i = 1; i < gAssociativity; i++)
  {
    if(lru[i*mNumBlocks/gAssociativity + line] > maxLRU)
    {
      lruIdx = i*mNumBlocks/gAssociativity + line;
      maxLRU = lru[lruIdx];
    }
  }
  theCache[lruIdx] = address/(gBlockSize*gWordSize);
  lru[lruIdx] = 0;
}

//==========================================================
// updateLRU()
// OVERVIEW: updates LRU
// MODIFIES: LRU
//==========================================================
void cache::updateLRU()
{
  for(int i = 0; i < mNumBlocks; i++)
  {
    lru[i]++;
  }
}

bool cache::getEnd()
{
  return theEnd;
}

unsigned int cache::getHits()
{
  return totalHits;
}

unsigned int cache::getAccesses()
{
  return totalAccesses;
}

cache::~cache()
{
}
